﻿using System;
using System.Text.RegularExpressions;
using System.Web;
using System.Linq;
using MethodWorx.CMS.Configuration;
using MethodWorx.CMS.Providers;
using MethodWorx.CMS.Content;
using MethodWorx.CMS.Services;
using MethodWorx.CMS.Plugins;
using MethodWorx.CMS.Administration;
using MethodWorx.CMS.Membership;
using MethodWorx.CMS.Workflow;

namespace MethodWorx.CMS.Web
{
    public class HostedUrlRewriter : IHttpModule
    {
        /// <summary>
        /// You will need to configure this module in the web.config file of your
        /// web and register it with IIS before being able to use it. For more information
        /// see the following link: http://go.microsoft.com/?linkid=8101007
        /// </summary>
        #region IHttpModule Members

        private CmsRequestContext pluginRequestContext = new CmsRequestContext();
        private PluginService pluginService;
        private PluginFactory plugins;

        public void Dispose()
        {
            //clean-up code here.
        }

        public void Init(HttpApplication context)
        {
            // Below is an example of how you can handle LogRequest event and provide 
            // custom logging implementation for it
            context.BeginRequest += new EventHandler(context_BeginRequest);
            context.EndRequest += new EventHandler(context_EndRequest);

            //
            //  ok, time to map the request via the CMS
            //  first things first, grab hold of the provider
            ICmsDataProvider provider = WebConfiguration.Instance.Provider;

            pluginService = new PluginService(provider.GetUnitOfWorkFactory(), provider.GetService<IPluginUserDataRepository>(), new PluginRepository(provider));
            plugins = new PluginFactory(provider.GetService<IApplicationSettingsRepository>(), new PluginRepository(provider));
        }

        void context_EndRequest(object sender, EventArgs e)
        {
            CmsRequestContext.Current.Dispose();
        }

        public static string GetVirtualPath(string url)
        {
            if (HttpContext.Current.Request.ApplicationPath == "/")
            {
                return "~" + url;
            }

            return Regex.Replace(url, "^" +
                           HttpContext.Current.Request.ApplicationPath + "(.+)$", "~$1");
        }


        void context_BeginRequest(object sender, EventArgs e)
        {
            //
            //  ok, time to map the request via the CMS
            //  first things first, grab hold of the provider
            ICmsDataProvider provider = WebConfiguration.Instance.Provider;

            //  get the service for accessing the content
            ContentService service = new ContentService(provider);
            UserService userService = new UserService(provider);


            bool processed = this.ProcessWithPlugins(plugins);

            if (!processed)
            {
                
                string[] parts = GetVirtualPath(HttpContext.Current.Request.RawUrl).TrimStart('~').Split(new string[] { "?" }, 2, StringSplitOptions.None);
                string[] pages = parts[0].Split(new string[] { "/" }, StringSplitOptions.RemoveEmptyEntries);

                if (pages.Length == 0)
                {
                    //
                    //  ok, lets get the URL
                    CmsRequestContext.Current.Page = service.GetRootPage();
                }
                else
                {
                    Page current = service.GetRootPage();
                    if (current != null)
                    {
                        foreach (string page in pages)
                        {
                            current = current.ChildPages.FindByName(page);
                            if (current == null)
                                break;
                        }
                        CmsRequestContext.Current.Page = current;
                    }
                }

                //
                //  ok, get the root page
                if (CmsRequestContext.Current.Page != null)
                {
                    bool css = false;
                    bool script = false;
                    css = HttpContext.Current.Request["__css"] != null;
                    script = HttpContext.Current.Request["__script"] != null;
                    
                    //
                    //  ok, before we run lets see if we have ay workflow
                    if(!css && !script)
                        this.ExecuteWorkflow(CmsRequestContext.Current);

                    var redirect = "~/Page.aspx";
                    try
                    {
                        if (HttpContext.Current.Request["preview"] != null)
                        {
                            PreviewSession session = userService.GetPreviewSession(Guid.Parse(HttpContext.Current.Request["preview"]));
                            CmsRequestContext.Current.PreviewSession = session;
                            if (session != null)
                            {
                                //
                                //  ok, we can preview
                                if (script)
                                {
                                    var response = HttpContext.Current.Response;
                                    response.ContentType = "text/javascript";
                                    response.Write(CmsRequestContext.Current.Page.GetScript() ?? "");
                                    response.End();
                                    return;
                                }
                                else if (css)
                                {
                                    var response = HttpContext.Current.Response;
                                    response.ContentType = "text/css";
                                    response.Write(CmsRequestContext.Current.Page.GetCss() ?? "");
                                    response.End();
                                    return;
                                }
                                else
                                    redirect = "~/PreviewPage.aspx";
                            }
                        }

                        if(script)
                        {
                            var response = HttpContext.Current.Response;
                            response.ContentType = "text/javascript";
                            response.Write(CmsRequestContext.Current.Page.GetScript() ?? "");
                            response.End();
                            return;
                        }
                        else  if(css)
                        {
                            var response = HttpContext.Current.Response;
                            response.ContentType = "text/css";
                            response.Write(CmsRequestContext.Current.Page.GetCss() ?? "");
                            response.End();
                            return;
                        }
                    }
                    catch(Exception ex)
                    {
                        //  swallow
                    }
                    HttpContext.Current.RewritePath(redirect);
                }
            }
            else
                HttpContext.Current.Response.End();
        }

        private void ExecuteWorkflow(CmsRequestContext cmsRequestContext)
        {
            var page = cmsRequestContext.Page;
            if (page != null)
            {
                if (!string.IsNullOrEmpty(HttpContext.Current.Request.HttpMethod))
                {
                    var workflow = page.GetWorkflowConfiguration(cmsRequestContext.Preview).FirstOrDefault(w => (w.InvokeOnHttpMethod ?? "").ToLower() == HttpContext.Current.Request.HttpMethod.ToLower());
                    if (workflow != null)
                    {
                        cmsRequestContext.WorkflowExecutionContext.AddWorkflowValue("currentPage", cmsRequestContext.Page);
                        cmsRequestContext.WorkflowExecutionContext.CmsDataProvider = WebConfiguration.Instance.Provider;

                        WorkflowExecutionEngine engine = new WorkflowExecutionEngine(
                            this.plugins.GetWorkflowComponents(),
                            cmsRequestContext.WorkflowExecutionContext,
                            workflow);

                        engine.Execute();
                    }
                }
            }
        }

        private bool ProcessWithPlugins(PluginFactory plugins)
        {
            bool processed = false;
            foreach (SafePlugin plugin in plugins.GetRequestPlugins())
            {
                //
                //  ok, process
                processed = plugin.Invoke<IRequestPlugin, bool>(p => p.ProcessRequest(new HttpContextWrapper(HttpContext.Current)));
                if (processed)
                    break;
            }

            return processed;
        }
        
        #endregion
    }
}
